package com.anuo.app.study.studymultithread;

import com.anuo.app.common.utils.ConcurrentUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath:spring/spring-context.xml"})
/**
 * 线程池研究(常用已掌握)
 * @ 线程池使用(没有返回值)
 * @ 线程池使用(有返回值)
 * @ 线程池创建更细粒度控制(比如设置最大排队任务数量,拒绝策略等) (注意, 一般用这种方式)
 */
public class StudyThreadPool {

    /**
     * 线程池使用,有返回值,有输入输出 (一般用这种) (已亲测有效)
     */
    @Test
    public void studyThreadPoolWithReturnValue() {
        int threadNum = Runtime.getRuntime().availableProcessors() * 2;//cup 核心数乘以2
        ExecutorService threadPool = ConcurrentUtil.createThreadPool(threadNum);//Executors.newFixedThreadPool(threadNum);
        List<Future<Integer>> list = new ArrayList<>();
        //发出100个并行运行请求给线程池
        for (int i = 0; i < 100; i++) {
            CalculationThread workThread = new CalculationThread(i);
            Future<Integer> future = threadPool.submit(workThread);
            list.add(future);

        }

        //获取结果(已google 大多数用这种 即再加个for来获取结果, 上个for 是启动并发运行任务)
        for (Future<Integer> future : list) {
            Integer result = new Integer("0");
            //取值并处理异常
            try {
                result = future.get();
            } catch (ExecutionException e) {
                e.getCause().printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
                future.cancel(true);  // interrupts if there is any possible error
            }
            System.out.println(result);
        }

        //关闭线程池
        threadPool.shutdown();
        //检测线程池是否关闭
        while (!threadPool.isTerminated()) {
        }
        //关闭了输出以下语句
        System.out.println("Finished all threads");
    }

    /**
     * 线程池使用,没有返回值 (一般不咋用这种方式)
     * 参考: (这文章不错)http://blog.csdn.net/u011240877/article/details/73440993?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
     */
    @Test
    public void studyThreadPoolNoReturnValue() {
        int threadNum = Runtime.getRuntime().availableProcessors() * 2;//cup 核心数乘以2
        ExecutorService threadPool = ConcurrentUtil.createThreadPool(threadNum);//Executors.newFixedThreadPool(threadNum);//池中的线程数为8
        //开100个任务丢到线程池由8个线程同时执行 (虽然程序看起来是8个线程同时执行, 实际同时执行的线程数应该是cpu支持的并发线程数比如 i5的2核4线程, 同时运行的线程是4个)
        for (int i = 0; i < 100; i++) {
            StudyMultithread.WorkThread workThread = new StudyMultithread.WorkThread(Integer.toString(i));

            threadPool.execute(workThread);
        }
        //关闭线程池
        threadPool.shutdown();
        //检测线程池是否关闭
        while (!threadPool.isTerminated()) {
        }
        //关闭了输出以下语句
        System.out.println("Finished all threads");
    }

    /**
     * 线程池创建更细粒度控制(一般用这种方式)
     */
    @Test
    public void studyThreadPoolDetailSetting() {

       //详细见下面方法注释
        ConcurrentUtil.createThreadPool(8);


    }

    /**
     * 为 studyThreadPoolWithReturnValue 服务
     */
    private static class CalculationThread implements Callable<Integer> {
        int input;

        public CalculationThread(int input) {
            this.input = input;
        }

        @Override
        public Integer call() throws Exception {
            Thread.sleep(1000);
            return input + 3;
        }
    }

    /**
     * ThreadLocal 线程本地变量
     * 其实就是线程内的局部变量，这个变量仅在这个线程的生命周期内起作用, 其他线程不可见
     * 参考: https://www.journaldev.com/1076/java-threadlocal-example
     */
    private static class StudyThreadLocal implements Runnable{

        //SimpleDateFormat is not thread-safe, so give one to each thread
        //线程安全的 SimpleDateFormat
        //ThreadLocal 变量建议用static 修饰这样在类的多个实例之间共享, 并且只占用一块内存
        private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue()
            {
                return new SimpleDateFormat("yyyyMMdd HHmm");
            }
        };

        public static void main(String[] args) throws InterruptedException {
            StudyThreadLocal obj = new StudyThreadLocal();
            for(int i=0 ; i<10; i++){
                Thread t = new Thread(obj, ""+i);
                Thread.sleep(new Random().nextInt(1000));
                t.start();
            }
        }

        @Override
        public void run() {
            System.out.println("Thread Name= "+Thread.currentThread().getName()
                    +" default Formatter = "+formatter.get().toPattern());
            try {
                Thread.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //formatter pattern is changed here by thread, but it won't reflect to other threads
            formatter.set(new SimpleDateFormat());

            System.out.println("Thread Name= "+Thread.currentThread().getName()
                    +" formatter = "+formatter.get().toPattern());
        }

    }

}

